#include "EditorScenePageViewModel.h"

#include "Scene/Scene.h"
#include "Asset/EditorDefaultPackage.h"
#include "PixelObject/PixelObjectMap.h"
#include "Widgets/ViewportWidget.h"
#include "Output/Console.h"

#include "Framework/Editor.h"
#include "Theme/EditorTheme.h"

void REditorScenePageViewModel::BindModel(TSharedObjectPtr<REditorPageModel> InModel)
{
    Super::BindModel(InModel);
    Model = InModel;

    Icon= REditorDefaultPackage::Get()->GetIcon(U"SceneIcon");
    Play= REditorDefaultPackage::Get()->GetIcon(U"PlayScene");
    Stop= REditorDefaultPackage::Get()->GetIcon(U"StopPlayScene");
    
    PixelObjectMap=NewObject<RPixelObjectMap>(this);
}

TSharedObjectPtr<RTexture2D> REditorScenePageViewModel::GetIcon() const
{
    return Icon;
}

CString REditorScenePageViewModel::GetTitle() const
{
    return Model->Scene->GetNodeName();
}

TSharedObjectPtr<RTexture2D> REditorScenePageViewModel::GetPlayButtonIcon() const
{
    if(Model->Scene->IsPlaying())
    {
        return Stop;
    }
    else
    {
        return Play;
    }
}

CString REditorScenePageViewModel::GetPlayButtonTooltip() 
{
    if(Model->Scene->IsPlaying())
    {
        return U"停止播放场景";
    }
    else
    {
        return U"播放场景";
    }
}

void REditorScenePageViewModel::OnPlayButtonClicked()
{
    if(Model->Scene->IsPlaying())
    {
        Model->Scene->EndPlay();
    }
    else
    {
        Model->Scene->BeginPlay();
    }
}

float REditorScenePageViewModel::GetOperationsWidth()
{
   return REditor::Get()->CurrentTheme->PageAreaButtonSize;
}

bool REditorScenePageViewModel::ShowAxis() const
{
    return Model->HasSelectedNode();
}

SVec3 REditorScenePageViewModel::GetAxisPosition() const
{
    if(Model->HasSelectedNode())
    {
        return Model->GetSelectedNode()->GetWorldPosition();
    }
    return SVec3();
}

SQuat REditorScenePageViewModel::GetAxisRotation() const
{
    if(Model->HasSelectedNode())
    {
        return Model->GetSelectedNode()->GetWorldRotation();
    }
    return SQuat();
}

float REditorScenePageViewModel::GetAxisScale() const
{
    //calculate the scale of axis
    //if the camera is far away, the axis should be bigger
    //if the camera is near, the axis should be smaller
    SVec3 AxisPosition=GetAxisPosition();

    float Distance=(AxisPosition-ViewPosition).GetLength();
    
    return Distance*0.1f;

}

void REditorScenePageViewModel::ViewMoveLeft()
{
    ViewPosition-=ViewRotation.GetRightDirection()*MoveSpeed;
}

void REditorScenePageViewModel::ViewMoveRight()
{
    ViewPosition+=ViewRotation.GetRightDirection()*MoveSpeed;
}

void REditorScenePageViewModel::ViewMoveForward()
{
    ViewPosition+=ViewRotation.GetForwardDirection()*MoveSpeed;
}

void REditorScenePageViewModel::ViewMoveBackward()
{
    ViewPosition-=ViewRotation.GetForwardDirection()*MoveSpeed;
}

void REditorScenePageViewModel::ViewMoveUp()
{
    ViewPosition+=ViewRotation.GetUpDirection()*MoveSpeed;
}

void REditorScenePageViewModel::ViewMoveDown()
{
    ViewPosition-=ViewRotation.GetUpDirection()*MoveSpeed;
}

void REditorScenePageViewModel::BeginDragView()
{
    InitRotationQuat=ViewRotation;
}

void REditorScenePageViewModel::OnDragView(const SVec2 &TotalDelta)
{
    float Pitch= -TotalDelta.Y*0.01f;
        Pitch=CMath::Clamp(Pitch, -CMathBase::HalfPi,CMathBase::HalfPi);
    float Yaw=-TotalDelta.X*0.01f;

    SQuat FinalRotation= SQuat::FromEuler(SVec3(0,0,Yaw))*InitRotationQuat*SQuat::FromEuler(SVec3(Pitch,0,0)) ;

    ViewRotation=FinalRotation;    
}

void REditorScenePageViewModel::BeginDragMoveAlongAxis(
    EAxis InAxis
    , const SVec3 &InMouseWorldPosition
    , const SVec3 &InMouseWorldDirection)
{
    auto TargetNode=Model->GetSelectedNode();
    assert(TargetNode);

    InitNodePosition=TargetNode->GetWorldPosition();
    DragAxisDirection=TargetNode->GetAxisDirection(InAxis);
    InitMouseWorldPosition=InMouseWorldPosition;
    InitMouseWorldDirection=InMouseWorldDirection;

    InitDragPoint=CalcDragPoint(InMouseWorldPosition,InMouseWorldDirection);

    return;

}

SVec3 REditorScenePageViewModel::CalcDragPoint(const SVec3 &InMouseWorldPosition, const SVec3 &InMouseWorldDirection)
{
    //计算拖拽点，拖拽点鼠标点击的点,是节点射线上与鼠标射线最近的点，
    //节点射线是节点位置到节点位置+轴方向的射线
    
    //节点射线方程L1: P=InitNodePosition+t0*DragAxisDirection
    //鼠标射线方程L1: P=InitMouseWorldPosition+t0*InitMouseWorldDirection
    //求节点射线与鼠标射线的最近点
    //这两条线并不一定相交，因为不可能正好点击了轴中心线，总会有一定的误差
    //先将节点投影到鼠标射线与坐标轴方向确定的平面上，保证这两条线在同一平面上
    
    //鼠标射线与坐标轴方向确定的平面
    SVec3 PlaneNormal=DragAxisDirection.CrossProduct(InMouseWorldDirection);
    SVec3 PointOnPlane=InitNodePosition;

    //设鼠标位置投影到平面的点为ProjectMousePosition,
    SVec3 ProjectMousePosition=CMath::ProjectPointToPlane(
        InMouseWorldPosition
        ,PointOnPlane
        ,PlaneNormal);

    //新的鼠标射线方程L2: P=ProjectMousePosition+t1*InMouseWorldDirection
    //新的鼠标射线方程与坐标轴射线方程的交点即为拖拽点
    SVec3 DragPoint;
    bool bIntersect=CMath::LineLineIntersection(
        InitNodePosition
        ,DragAxisDirection
        ,ProjectMousePosition
        ,InMouseWorldDirection
        ,DragPoint
    );
    
    if(!bIntersect)
    {
        //节点射线几乎平行于鼠标射线
        //此时拖拽会产生无穷远的拖拽点，这种情况下，拖拽点应该是节点位置
        DragPoint=InitNodePosition;
    }

    return DragPoint;
}

TSharedObjectPtr<RPixelObjectMap> REditorScenePageViewModel::GetPixelObjectMap(TSharedObjectPtr<RViewportWidget> InViewport)
{
    //尽量减少不必要的渲染
    if(ViewportFormerSize!= InViewport->PresentSize)
    {
        //视口大小正在改变，当改变大小时，
        //会导致像素对象映射频繁重新创建渲染端对象
        //严重影响性能，所以当视口大小改变时，不渲染
        //此时，鼠标正在拖拽视口边框，也没必要渲染
        ViewportFormerSize=InViewport->PresentSize;
        return nullptr;
    }

    if(!InViewport->IsCurrentMouseIn())
    {
        //如果鼠标不在视口内，就不渲染，这样可以提高性能
        return nullptr;
    }




    return PixelObjectMap;

}

void REditorScenePageViewModel::OnDragMoveAlongAxis(
    const SVec3 &InMouseWorldPosition
    , const SVec3 &InMouseWorldDirection)
{
    //计算新的拖拽点
    SVec3 NewDragPoint=CalcDragPoint(InMouseWorldPosition,InMouseWorldDirection);

    //计算拖拽距离
    SVec3 Delta=NewDragPoint-InitDragPoint;

    //移动节点
    auto TargetNode=Model->GetSelectedNode();
    assert(TargetNode);
    TargetNode->SetWorldPosition(InitNodePosition+Delta);
}


void REditorScenePageViewModel::BeginMouseDrag(TSharedObjectPtr<RViewportWidget> InViewport)
{
    //检查是否点击到轴
    auto MousePosition=InViewport->GetMousePositionInWidget();
    auto PixelObject=InViewport->QueryPixelObject(MousePosition);
    if(PixelObject)
    {
        if(PixelObject->ObjectType==EPixelObjectType::Axis)
        {
            DragType=EDragType::MoveAlongAxis;
            
            std::shared_ptr<CAxisPixelObject> AxisObject=std::static_pointer_cast<CAxisPixelObject>(PixelObject);
                
            SVec3 MouseWorldPosition;
            SVec3 MouseWorldDirection;
            MouseWorldPosition=InViewport->ViewportToScene(MousePosition,MouseWorldDirection);
            
            BeginDragMoveAlongAxis(AxisObject->AxisType
                ,MouseWorldPosition
                ,MouseWorldDirection);

            return;
        
        }
    }

    //如果没有点击到轴，则开始拖拽视角
    DragType=EDragType::View;
    BeginDragView();
}

void REditorScenePageViewModel::OnMouseDrag(TSharedObjectPtr<RViewportWidget> InViewport, const SVec2 &TotalDelta)
{
    switch(DragType)
    {
    case EDragType::None:
        //啥也不干
        break;

    case EDragType::View:
        OnDragView(TotalDelta);
        break;
    case EDragType::MoveAlongAxis:
        {
            SVec3 MouseWorldPosition;
            SVec3 MouseWorldDirection;
            MouseWorldPosition=InViewport->ViewportToScene(InViewport->GetMousePositionInWidget(),MouseWorldDirection);
            OnDragMoveAlongAxis(MouseWorldPosition,MouseWorldDirection);
        }
        break;
    
    default:
        assert(false);
    }
}

void REditorScenePageViewModel::EndMouseDrag()
{
    DragType=EDragType::None;
}

TSharedObjectPtr<RScene> REditorScenePageViewModel::GetScene() const
{
    return Model->Scene;
}
